<template>
  <el-scrollbar
    :key="menuId"
    tag="ul"
    role="menu"
    :class="ns.b()"
    :wrap-class="ns.e('wrap')"
    :view-class="[ns.e('list'), ns.is('empty', isEmpty)]"
    @mousemove="handleMouseMove"
    @mouseleave="clearHoverZone"
  >
    <el-cascader-node
      v-for="node in nodes"
      :key="node.uid"
      :node="node"
      :menu-id="menuId"
      @expand="handleExpand"
    />
    <div v-if="isLoading" :class="ns.e('empty-text')">
      <el-icon size="14" :class="ns.is('loading')">
        <loading />
      </el-icon>
      {{ t('el.cascader.loading') }}
    </div>
    <div v-else-if="isEmpty" :class="ns.e('empty-text')">
      <slot name="empty">{{ t('el.cascader.noData') }}</slot>
    </div>
    <!-- eslint-disable-next-line vue/html-self-closing -->
    <svg
      v-else-if="panel?.isHoverMenu"
      ref="hoverZone"
      :class="ns.e('hover-zone')"
    ></svg>
  </el-scrollbar>
</template>

<script lang="ts" setup>
import { computed, getCurrentInstance, inject, ref } from 'vue'
import ElScrollbar from '@element-plus/components/scrollbar'
import { useId, useLocale, useNamespace } from '@element-plus/hooks'
import { Loading } from '@element-plus/icons-vue'
import ElIcon from '@element-plus/components/icon'
import ElCascaderNode from './node.vue'
import { CASCADER_PANEL_INJECTION_KEY } from './types'

import type { CascaderNode } from './types'
import type { PropType } from 'vue'

defineOptions({
  name: 'ElCascaderMenu',
})

const props = defineProps({
  nodes: {
    type: Array as PropType<CascaderNode[]>,
    required: true,
  },
  index: {
    type: Number,
    required: true,
  },
})

const instance = getCurrentInstance()!
const ns = useNamespace('cascader-menu')

const { t } = useLocale()
const id = useId()
let activeNode: HTMLElement
let hoverTimer: number | undefined

const panel = inject(CASCADER_PANEL_INJECTION_KEY)!

const hoverZone = ref<SVGSVGElement>()

const isEmpty = computed(() => !props.nodes.length)
const isLoading = computed(() => !panel.initialLoaded)
const menuId = computed(() => `${id.value}-${props.index}`)

const handleExpand = (e: MouseEvent) => {
  activeNode = e.target as HTMLElement
}

const handleMouseMove = (e: MouseEvent) => {
  if (!panel.isHoverMenu || !activeNode || !hoverZone.value) return

  if (activeNode.contains(e.target as HTMLElement)) {
    clearHoverTimer()

    const el = instance.vnode.el as HTMLElement
    const { left } = el.getBoundingClientRect()
    const { offsetWidth, offsetHeight } = el
    const startX = e.clientX - left
    const top = activeNode.offsetTop
    const bottom = top + activeNode.offsetHeight
    const scrollTop = el.querySelector(`.${ns.e('wrap')}`)?.scrollTop || 0

    hoverZone.value.innerHTML = `
          <path style="pointer-events: auto;" fill="transparent" d="M${startX} ${top} L${offsetWidth} ${scrollTop} V${top} Z" />
          <path style="pointer-events: auto;" fill="transparent" d="M${startX} ${bottom} L${offsetWidth} ${offsetHeight + scrollTop} V${bottom} Z" />
        `
  } else if (!hoverTimer) {
    hoverTimer = window.setTimeout(clearHoverZone, panel.config.hoverThreshold)
  }
}

const clearHoverTimer = () => {
  if (!hoverTimer) return
  clearTimeout(hoverTimer)
  hoverTimer = undefined
}

const clearHoverZone = () => {
  if (!hoverZone.value) return
  hoverZone.value.innerHTML = ''
  clearHoverTimer()
}
</script>
